/*
 *  Copyright (C) 2002-2010  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

//#include "adlib.h"
//#include "dosbox.h"

#ifndef _DBOPL_H_
#define _DBOPL_H_
#include <stdint.h>
typedef signed int Bits;
typedef unsigned int Bitu;
typedef int8_t Bit8s;
typedef uint8_t Bit8u;
typedef int16_t Bit16s;
typedef uint16_t Bit16u;
typedef int32_t Bit32s;
typedef uint32_t Bit32u;

#define INLINE inline

#define GCC_UNLIKELY(x) (x)

// Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
#define WAVE_HANDLER 10
// Use a logarithmic wavetable with an exponential table for volume
#define WAVE_TABLELOG 11
// Use a linear wavetable with a multiply table for volume
#define WAVE_TABLEMUL 12

// Select the type of wave generator routine
#define DBOPL_WAVE WAVE_TABLEMUL

namespace DBOPL {

struct Chip;
struct Operator;
struct Channel;

#if (DBOPL_WAVE == WAVE_HANDLER)
typedef Bits(DB_FASTCALL *WaveHandler)(Bitu i, Bitu volume);
#endif

typedef Bits (DBOPL::Operator::*VolumeHandler)();
typedef Channel *(DBOPL::Channel::*SynthHandler)(Chip *chip, Bit32u samples, Bit32s *output);

// Different synth modes that can generate blocks of data
typedef enum {
        sm2AM,
        sm2FM,
        sm3AM,
        sm3FM,
        sm4Start,
        sm3FMFM,
        sm3AMFM,
        sm3FMAM,
        sm3AMAM,
        sm6Start,
        sm2Percussion,
        sm3Percussion,
} SynthMode;

// Shifts for the values contained in chandata variable
enum {
        SHIFT_KSLBASE = 16,
        SHIFT_KEYCODE = 24,
};

struct Operator {
    public:
        // Masks for operator 20 values
        enum {
                MASK_KSR = 0x10,
                MASK_SUSTAIN = 0x20,
                MASK_VIBRATO = 0x40,
                MASK_TREMOLO = 0x80,
        };

        typedef enum {
                OFF,
                RELEASE,
                SUSTAIN,
                DECAY,
                ATTACK,
        } State;

        VolumeHandler volHandler;

#if (DBOPL_WAVE == WAVE_HANDLER)
        WaveHandler waveHandler; // Routine that generate a wave
#else
        Bit16s *waveBase;
        Bit32u waveMask;
        Bit32u waveStart;
#endif
        Bit32u waveIndex;   // WAVE_BITS shifted counter of the frequency index
        Bit32u waveAdd;     // The base frequency without vibrato
        Bit32u waveCurrent; // waveAdd + vibratao

        Bit32u chanData;     // Frequency/octave and derived data coming from whatever channel controls this
        Bit32u freqMul;      // Scale channel frequency with this, TODO maybe remove?
        Bit32u vibrato;      // Scaled up vibrato strength
        Bit32s sustainLevel; // When stopping at sustain level stop here
        Bit32s totalLevel;   // totalLevel is added to every generated volume
        Bit32u currentLevel; // totalLevel + tremolo
        Bit32s volume;       // The currently active volume

        Bit32u attackAdd; // Timers for the different states of the envelope
        Bit32u decayAdd;
        Bit32u releaseAdd;
        Bit32u rateIndex; // Current position of the evenlope

        Bit8u rateZero; // Bits for the different states of the envelope having no changes
        Bit8u keyOn;    // Bitmask of different values that can generate keyon
        // Registers, also used to check for changes
        Bit8u reg20, reg40, reg60, reg80, regE0;
        // Active part of the envelope we're in
        Bit8u state;
        // 0xff when tremolo is enabled
        Bit8u tremoloMask;
        // Strength of the vibrato
        Bit8u vibStrength;
        // Keep track of the calculated KSR so we can check for changes
        Bit8u ksr;

    private:
        void SetState(Bit8u s);
        void UpdateAttack(const Chip *chip);
        void UpdateRelease(const Chip *chip);
        void UpdateDecay(const Chip *chip);

    public:
        void UpdateAttenuation();
        void UpdateRates(const Chip *chip);
        void UpdateFrequency();

        void Write20(const Chip *chip, Bit8u val);
        void Write40(const Chip *chip, Bit8u val);
        void Write60(const Chip *chip, Bit8u val);
        void Write80(const Chip *chip, Bit8u val);
        void WriteE0(const Chip *chip, Bit8u val);

        bool Silent() const;
        void Prepare(const Chip *chip);

        void KeyOn(Bit8u mask);
        void KeyOff(Bit8u mask);

        template <State state> Bits TemplateVolume();

        Bit32s RateForward(Bit32u add);
        Bitu ForwardWave();
        Bitu ForwardVolume();

        Bits GetSample(Bits modulation);
        Bits GetWave(Bitu index, Bitu vol);

    public:
        Operator();
};

struct Channel {
        Operator op[2];
        inline Operator *Op(Bitu index) { return &((this + (index >> 1))->op[index & 1]); }
        SynthHandler synthHandler;
        Bit32u chanData; // Frequency/octave and derived values
        Bit32s old[2];   // Old data for feedback

        Bit8u feedback; // Feedback shift
        Bit8u regB0;    // Register values to check for changes
        Bit8u regC0;
        // This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
        Bit8u fourMask;
        Bit8s maskLeft; // Sign extended values for both channel's panning
        Bit8s maskRight;

        // Forward the channel data to the operators of the channel
        void SetChanData(const Chip *chip, Bit32u data);
        // Change in the chandata, check for new values and if we have to forward to operators
        void UpdateFrequency(const Chip *chip, Bit8u fourOp);
        void WriteA0(const Chip *chip, Bit8u val);
        void WriteB0(const Chip *chip, Bit8u val);
        void WriteC0(const Chip *chip, Bit8u val);
        void ResetC0(const Chip *chip);

        // call this for the first channel
        template <bool opl3Mode> void GeneratePercussion(Chip *chip, Bit32s *output);

        // Generate blocks of data in specific modes
        template <SynthMode mode> Channel *BlockTemplate(Chip *chip, Bit32u samples, Bit32s *output);
        Channel();
};

struct Chip {
        // This is used as the base counter for vibrato and tremolo
        Bit32u lfoCounter;
        Bit32u lfoAdd;

        Bit32u noiseCounter;
        Bit32u noiseAdd;
        Bit32u noiseValue;

        // Frequency scales for the different multiplications
        Bit32u freqMul[16];
        // Rates for decay and release for rate of this chip
        Bit32u linearRates[76];
        // Best match attack rates for the rate of this chip
        Bit32u attackRates[76];

        // 18 channels with 2 operators each
        Channel chan[18];

        Bit8u reg104;
        Bit8u reg08;
        Bit8u reg04;
        Bit8u regBD;
        Bit8u vibratoIndex;
        Bit8u tremoloIndex;
        Bit8s vibratoSign;
        Bit8u vibratoShift;
        Bit8u tremoloValue;
        Bit8u vibratoStrength;
        Bit8u tremoloStrength;
        // Mask for allowed wave forms
        Bit8u waveFormMask;
        // 0 or -1 when enabled
        Bit8s opl3Active;

        int is_opl3;

        // Return the maximum amount of samples before and LFO change
        Bit32u ForwardLFO(Bit32u samples);
        Bit32u ForwardNoise();

        void WriteBD(Bit8u val);
        void WriteReg(Bit32u reg, Bit8u val);

        Bit32u WriteAddr(Bit32u port, Bit8u val);

        void GenerateBlock2(Bitu samples, Bit32s *output);
        void GenerateBlock3(Bitu samples, Bit32s *output);

        void Generate(Bit32u samples);
        void Setup(Bit32u r, int chip_is_opl3);

        Chip();
};

/*struct Handler : public Adlib::Handler {
        DBOPL::Chip chip;
        virtual Bit32u WriteAddr( Bit32u port, Bit8u val );
        virtual void WriteReg( Bit32u addr, Bit8u val );
        virtual void Generate( MixerChannel* chan, Bitu samples );
        virtual void Init( Bitu rate );
};*/

void InitTables(void);

}; // namespace DBOPL

#endif /* _DBOPL_H_ */
